﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Text;
using System.Net;
using System.Net.Sockets;
using Common.Logging;
using Framework.Message.SMS.SGIP.Commands;
using Framework.Core.Queue;
using System.Threading;
using Framework.Message.SMS.CommonLibrary;

namespace Framework.Message.SMS.SGIP
{
    public class SGIPClient : ISMSClient
    {
        #region 变量与属性
        /// <summary>
        /// 网关设置
        /// </summary>
        private SGIPClientGatewaySetting _Setting = null;
        /// <summary>
        /// 
        /// </summary>
        private int _PingTick = 5;
        /// <summary>
        /// 超时
        /// </summary>
        private int _TimeoutTick = 50;
        /// <summary>
        /// 发命令套字节
        /// </summary>
        private Socket _SendCommandSocket = null;
        //private Socket _DeliveCommandSocket = null;
        /// <summary>
        /// 本服务是否在运行
        /// </summary>
        private bool _IsStarted = false;
        /// <summary>
        /// 是否已经登录    
        /// </summary>
        private bool _IsLogin = false;
        /// <summary>
        /// 日志组件
        /// </summary>
        private ILog _Logger = LogManager.GetLogger("SGIPClient");
        /// <summary>
        /// 最后正确发送消息时间
        /// </summary>
        private DateTime _LastActiveTime;

        private uint _LastMsgSequence = 0;

        /// <summary>
        /// 得到下一个排队序号
        /// </summary>
        /// <returns></returns>
        private CommandSequence NextSequence
        {
            get
            {
                lock (this)
                {
                    try
                    {
                        this._LastMsgSequence++;
                    }
                    catch (OverflowException e)
                    {
                        _Logger.Error("序列号溢出。", e);
                        _LastMsgSequence = uint.MinValue;
                    }
                }
                CommandSequence seq = new CommandSequence(_Setting.SPNumber, DateTime.Now, _LastMsgSequence);
                return seq;
            }
        }

        private DateTime _LastPingTime = DateTime.Now;
        /// <summary>
        /// 是否到了ping一次的时间
        /// </summary>
        private bool IsPingTime
        {
            get
            {
                TimeSpan ts = (DateTime.Now - this._LastPingTime);
                return ts.TotalSeconds >= _PingTick;
            }
        }

        private bool IsSocketActive
        {
            get
            {
                TimeSpan ts = (DateTime.Now - this._LastActiveTime);
                return ts.TotalSeconds < _TimeoutTick;
            }
        }

        #region 线程
        /// <summary>
        /// 链接监测线程
        /// </summary>
        private BackgroundWorker _MonitoringThread = null;
        /// <summary>
        /// 发送命令线程
        /// </summary>
        private BackgroundWorker _SendCommandThread = null;
        /// <summary>
        /// 接收命令线程
        /// </summary>
        private BackgroundWorker _ReceiveCommandThread = null;

        #endregion

        #region 队列
        /// <summary>
        /// 待发送队列
        /// </summary>
        private IStatusQueue<string, Command, CommandInQueueStatus> _CommandQueue = new BaseStatusQueue<string, Command, CommandInQueueStatus>();
        ///// <summary>
        ///// 已发送队列
        ///// </summary>
        //private IStatusQueue<string, BaseCommand, CommandInQueueStatus> _SentCommandQueue = new BaseStatusQueue<string, BaseCommand, CommandInQueueStatus>();
        #endregion
        #endregion

        #region 事件
        /// <summary>
        /// 当登录前
        /// </summary>
        public event EventHandler<LoginingEventArgs> Logining;
        protected void OnLogining(LoginingEventArgs e)
        {
            if (Logining != null)
            {
                Logining(this, e);
            }
        }

        /// <summary>
        /// 当登录后
        /// </summary>
        public event EventHandler<CommandRespEventArgs<BindRespCommand>> Logined;
        protected void OnLogined(CommandRespEventArgs<BindRespCommand> e)
        {
            if (Logined != null)
            {
                Logined(this, e);
            }
        }

        public event EventHandler<EventArgs> Logouted;
        protected void OnLogouted(EventArgs e)
        {
            if (Logouted != null)
            {
                Logouted(this, e);
            }
        }
        #endregion

        #region 函数
        public SGIPClient(SGIPClientGatewaySetting gatewaySetting)
        {
            _Setting = gatewaySetting;
        }
        #region 私有函数
        private void SendCommand(Command command)
        {
            _SendCommandSocket.Send(command.ToBytes());
            this._LastActiveTime = DateTime.Now;
        }

        private void StopClient()
        {
            lock (this)
            {
                this._IsStarted = false;
            }
        }

        private void Login()
        {
            if (this._IsLogin)
            {
                return;
            }
            try
            {
                BindCommand command = CommandFactory.GetNewCommand<BindCommand>(this.NextSequence);
                if (command != null)
                {
                    command.LoginName = _Setting.UserName;
                    command.LoginPassowrd = _Setting.Password;
                    command.LoginType = _Setting.LoginType;
                    OnLogining(new LoginingEventArgs());
                    SendCommand(command);
                    _Logger.Debug("发送登录命令。");
                }
            }
            catch (Exception ex)
            {
                //发送出错
                _Logger.Error("登录网关异常：", ex);
                throw ex;
            }
            DateTime t1 = DateTime.Now;
            _Logger.Debug("等待登录命令回复。");
            while (!this._IsLogin)
            {
                byte[] rbuf = new Byte[50];
                int l;
                try
                {
                    l = this._SendCommandSocket.Receive(rbuf);
                    if (l >= 29)
                    {
                        if (ByteIntConvert.Bytes2UInt(rbuf, 4) == (uint)SGIPCommandType.Bind_Resp)
                        {
                            BindRespCommand resp = new BindRespCommand(rbuf);
                            CommandRespEventArgs<BindRespCommand> arg = new CommandRespEventArgs<BindRespCommand>();
                            arg.RespErrorCode = resp.Result;
                            OnLogined(arg);
                            if (resp.Result == ErrorCode.Success)
                            {
                                this._IsLogin = true;
                                _Logger.Debug("登录成功。");
                            }
                            else
                            {
                                this._IsLogin = false;
                                _Logger.Debug(string.Format("登录失败。错误代码", resp.Result));
                            }
                            break;
                        }
                    }
                    this._LastActiveTime = DateTime.Now;  //更新当前最后成功收发套接字的时间
                }
                catch (Exception ex)
                {
                    _Logger.Error("登录网关异常：", ex);
                    throw ex;
                }
                System.TimeSpan t = DateTime.Now - t1;
                if (t.TotalSeconds > 10)
                {
                    break;
                }
                Thread.Sleep(50);
            }
        }

        private void Ping()
        {
            PingCommand command = CommandFactory.GetNewCommand(SGIPCommandType.Ping, this.NextSequence) as PingCommand;
            if (command != null)
            {
                this.SendCommand(command);
                this._Logger.Debug("发送了Ping命令。");
                this._CommandQueue.Add(command.Header.SequenceNumber.ToString(), command, CommandInQueueStatus.Sent);
                _Logger.Debug(string.Format("命令【{0}】进入已发送队列中。", command.Header.SequenceNumber.ToString()));
                this._LastPingTime = DateTime.Now;
            }
        }

        #region 线程处理函数
        private void ThreadsStart()
        {
            _Logger.Debug("启动线程。");
            _MonitoringThread = new BackgroundWorker();
            _MonitoringThread.DoWork += new DoWorkEventHandler(_MonitoringThread_DoWork);
            _MonitoringThread.RunWorkerAsync();

            _SendCommandThread = new BackgroundWorker();
            _SendCommandThread.DoWork += new DoWorkEventHandler(_SendCommandThread_DoWork);

            _ReceiveCommandThread = new BackgroundWorker();
            _ReceiveCommandThread.DoWork += new DoWorkEventHandler(_ReceiveCommandThread_DoWork);
            _ReceiveCommandThread.RunWorkerAsync();
        }

        private void _ReceiveCommandThread_DoWork(object sender, DoWorkEventArgs e)
        {
            List<byte> receiveBuf = new List<byte>();
            while (this._IsStarted)
            {
                try
                {
                    byte[] thisBuf = new byte[10240];
                    int thisMsgLength = this._SendCommandSocket.Receive(thisBuf);
                    _Logger.Debug(string.Format("本次收到{0}个字节。", thisMsgLength));
                    if (thisMsgLength > 0)
                    {
                        for (int i = 0; i < thisMsgLength; i++)
                        {
                            receiveBuf.Add(thisBuf[i]);
                        }
                        _Logger.Debug(string.Format("{0}个字节数据被加到接收缓存里。缓存里还存在{1}个字节。", thisMsgLength, receiveBuf.Count));
                        //先取命令头信息获取命令基本信息。
                        if (receiveBuf.Count >= CommandHeader.HeaderLength)
                        {
                            byte[] headerBytes = new byte[CommandHeader.HeaderLength];
                            for (int i = 0; i < CommandHeader.HeaderLength; i++)
                            {
                                headerBytes[i] = receiveBuf[i];
                            }
                            CommandHeader header = new CommandHeader(headerBytes);
                            _Logger.Debug(string.Format("收到的命令类型为{0}，长度为{1}，序列为。", header.CommandID, header.MessageLength, header.SequenceNumber.ToString()));
                            if (receiveBuf.Count >= header.MessageLength)
                            {
                                byte[] commandBytes = new byte[header.MessageLength];
                                for (int i = 0; i < header.MessageLength; i++)
                                {
                                    commandBytes[i] = receiveBuf[0];
                                    receiveBuf.RemoveAt(0);
                                }

                                Command command = CommandFactory.GetCommandFormByte(commandBytes);

                                if (command != null)
                                {
                                    
                                }
                                this._CommandQueue.Remove(header.SequenceNumber.ToString());
                                _Logger.Debug(string.Format("命令【{0}】从已发送队列中移出。", header.SequenceNumber.ToString()));
                            }
                        }
                    }
                }
                catch (Exception ex)
                {
                    _Logger.Debug("接收数据线程发生异常。", ex);
                    this._IsStarted = false;
                }
                Thread.Sleep(50);
            }
            _Logger.Debug("接收数据线程结束。");
        }

        private void _SendCommandThread_DoWork(object sender, DoWorkEventArgs e)
        {
            while (this._IsStarted)
            {
                if (this._IsLogin)
                {
                    List<CommandInQueueStatus> status = new List<CommandInQueueStatus>();
                    status.Add(CommandInQueueStatus.New);
                    IList<IStatusQueuer<string, Command, CommandInQueueStatus>> list = this._CommandQueue.GetTopNQueuers(16, status);
                    foreach (IStatusQueuer<string, Command, CommandInQueueStatus> item in list)
                    {
                        #region 发送信息
                        switch (item.Value.Header.CommandID)
                        {
                            case SGIPCommandType.Unbind:
                                break;
                            case SGIPCommandType.Unbind_Resp:
                                break;
                            case SGIPCommandType.Submit:
                                lock (this)
                                {
                                    item.Status = CommandInQueueStatus.Sending;
                                }
                                this.SendCommand(item.Value);
                                break;
                            case SGIPCommandType.Deliver_Resp:
                                lock (this)
                                {
                                    item.Status = CommandInQueueStatus.Sent;
                                }
                                this.SendCommand(item.Value);
                                break;
                            case SGIPCommandType.Report:
                                lock (this)
                                {
                                    item.Status = CommandInQueueStatus.Sending;
                                }
                                this.SendCommand(item.Value);
                                break;
                            default:
                                break;
                        }
                        #endregion
                    }
                }
            }
        }

        private void _MonitoringThread_DoWork(object sender, DoWorkEventArgs e)
        {
            while (this._IsStarted)
            {
                if (IsSocketActive)
                {
                    if (this.IsPingTime)
                    {
                        this.Ping();
                    }
                }
                else
                {
                    this._IsStarted = false;
                }
                Thread.Sleep(50);
            }
            _Logger.Debug("连接检测线程结束。");
        }
        #endregion
        #endregion

        #endregion

        #region ISMSClient Members

        public void Start()
        {
            if (!this._IsStarted)
            {
                if (_Setting == null)
                {
                    _Logger.Error("请先配置模块。");
                    throw new ApplicationException("请先配置模块。");
                }

                _Logger.Debug("准备开启服务。");
                try
                {
                    this._SendCommandSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                    IPHostEntry ipHE = Dns.GetHostEntry(_Setting.ServiceIP);
                    IPEndPoint ipEP = new IPEndPoint(ipHE.AddressList[0], _Setting.MTPort);
                    this._SendCommandSocket.Connect(ipEP);
                }
                catch (System.Exception ex)
                {
                    _Logger.Error("Socket Error。", ex);
                    throw ex;
                }
                _Logger.Debug("Socket连接成功。");
                this.Login();
                this.ThreadsStart();
                this._IsStarted = true;
            }
        }

        public void Stop()
        {
        }
        #endregion
    }
}
